﻿using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace StarMongoDbHelper
{
    /// <summary>
    /// MongoDB封装操作类
    /// </summary>
    public class DBHelper : DBBase
    {
        /// <summary>
        /// MongoDB数据库连接字符串
        /// "mongodb://root:pwd@127.0.0.1:27017"
        /// </summary>
        public static string DbConnStr { get; set; }
        /// <summary>
        /// 数据库名称
        /// </summary>
        public static string DbName { get; set; }
        /// <summary>
        /// 数据库连接
        /// </summary>
        public class DBClient
        {
            public MongoDbHelper Value { get; set; }
            public DBConn Config { get; set; }
        }
        public DBHelper(string dbConnStr, string dbName)
        {
            DbConnStr = dbConnStr;
            DbName = dbName;
        }
        #region 初始化数据库链接
        /// <summary>
        /// 初始化数据库链接
        /// </summary>
        /// <param name="entityname"></param>
        /// <returns></returns>
        private DBClient InitDBConn(string entityname)
        {
            var __dbconn = new DBClient();
            __dbconn.Config = new DBConn
            {
                conn = DbConnStr,
                dbname = DbName,
                entityname = entityname
            };
            if (__dbconn.Value == null)
            {
                __dbconn.Value = new MongoDbHelper(DbConnStr, DbName);
            }
            return __dbconn;
        }
        #endregion

        #region 获取列表
        /// <summary>
        /// 获取列表
        /// db.List("users", new QueryListInput() { page = 1, psize = 50, filter = "{formby:{$in:[1,2]}}", fields = "['name','sex']" }).Result;
        /// db.List("users",JObject.FromObject({page:1,psize:1000,filter:JSON.stringify({"_id":id}),fields:JSON.stringify(['_id'])})).Result
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="params"></param>
        /// <returns></returns>
        public async Task<QueryListOutput> List(string entityname, QueryListInput @params)
        {
            return await Task.Run(() =>
            {
                var __db = InitDBConn(entityname);
                QueryListOutput output = new QueryListOutput();
                output.param = @params;
                if (output.param.page == 0)
                {
                    output.param.page = 1;
                }
                if (output.param.psize == 0)
                {
                    output.param.psize = 10000;
                }
                if (string.IsNullOrEmpty(output.param.filter))
                {
                    output.param.filter = "{}";
                }

                var queryfilter = FormatFilter(output.param.filter);
                var querysort = output.param.sort;
                if (string.IsNullOrEmpty(querysort) || querysort == "{}")
                {
                    querysort = "{\"_id\":-1}";
                }
                else
                {
                    if (querysort.StartsWith("["))
                    {
                        var sortja = JArray.Parse(querysort).ToObject<List<OrderByDto>>();
                        var sortjo = new JObject();
                        for (var i = 0; i < sortja.Count; i++)
                        {
                            var it = sortja[i];
                            sortjo[it.key] = (it.desc ? -1 : 1);
                        }
                        if (sortja.Count > 0)
                        {
                            querysort = sortjo.ToString(Newtonsoft.Json.Formatting.None, null);
                        }
                        else
                        {
                            querysort = "{\"_id\":-1}";
                        }
                    }
                }
                FilterDefinition<BsonDocument> ufilter = queryfilter;


                if (output.param.page <= 0)
                {
                    output.param.page = 1;
                }
                if (output.param.page == 1 || output.param.gettotalcount)
                {
                    output.param.total = __db.Value.GetCount(__db.Config.entityname, ufilter);
                    output.param.totalPages = (int)Math.Ceiling(output.param.total / (double)output.param.psize);
                }

                SortDefinition<BsonDocument> _sort = querysort;

                IList<BsonDocument> list;

                ProjectionDefinition<BsonDocument> projectionDefinition = "{\"_id\":1}";
                bool usproj = false;
                if (!string.IsNullOrEmpty(output.param.fields))
                {
                    if (output.param.fields.StartsWith("["))
                    {
                        ProjectionDefinitionBuilder<BsonDocument> builderProjection = Builders<BsonDocument>.Projection;

                        List<string> fields = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>>(output.param.fields);

                        if (fields.Count > 0)
                        {
                            usproj = true;
                            BsonDocument projfields = new BsonDocument();
                            foreach (var field in fields)
                            {
                                projfields[field] = 1;
                            }
                            projfields["_id"] = 1;
                            projectionDefinition = projfields;
                        }

                    }
                    else
                    {
                        usproj = true;
                        projectionDefinition = output.param.fields;
                    }

                }
                if (usproj)
                {
                    list = __db.Value.GetPagedDocumentsByFilter<BsonDocument>(__db.Config.entityname, ufilter, projectionDefinition, _sort, output.param.page, output.param.psize);
                }
                else
                {
                    list = __db.Value.GetPagedDocumentsByFilter<BsonDocument>(__db.Config.entityname, ufilter, _sort, output.param.page, output.param.psize);
                }
                output.rows = parseToJArray(list);

                if (@params.sumfields != null)
                {
                    List<string> fields = JArray.FromObject(@params.sumfields).ToObject<List<string>>();
                    if (fields.Count > 0)
                    {
                        output.sumdata = Sums(entityname, queryfilter, null, @params.sumfields, "").Result;
                    }

                }
                return output;
            });
        }
        #endregion

        #region 获取单条数据
        /// <summary>
        /// 通过_id获取数据
        /// db.Get("users","xxxxxxx",fields).Result
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="_id"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        public async Task<JObject> Get(string entityname, string _id, List<string> fields = null)
        {
            return await Task.Run(() =>
            {
                var __db = InitDBConn(entityname);
                if (fields != null && fields.Count > 0)
                {
                    ProjectionDefinitionBuilder<BsonDocument> builderProjection = Builders<BsonDocument>.Projection;

                    BsonDocument projfields = new BsonDocument();

                    foreach (var field in fields)
                    {
                        projfields[field] = 1;
                    }
                    projfields["_id"] = 1;
                    MongoDB.Driver.ProjectionDefinition<BsonDocument> projectionDefinition = projfields;

                    var ent = __db.Value.GetDocumentById<BsonDocument>(__db.Config.entityname, _id, projectionDefinition);
                    return parseToJObject(ent);
                }
                else
                {
                    return parseToJObject(__db.Value.GetDocumentById<BsonDocument>(__db.Config.entityname, _id));

                }
            });
        }
        /// <summary>
        /// 通过过滤条件获取一条数据
        /// db.GetOne("users",JObject.FromObject({username:'yyfs'}),fields).Result
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="filter"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        public async Task<JObject> GetOne(string entityname, JObject filter, List<string> fields = null)
        {
            return await Task.Run(() =>
            {
                var __db = InitDBConn(entityname);
                var queryfilter = FormatFilter(filter.ToString(Newtonsoft.Json.Formatting.None, null));
                FilterDefinition<BsonDocument> ufilter = queryfilter;
                if (fields.Count > 0)
                {
                    ProjectionDefinitionBuilder<BsonDocument> builderProjection = Builders<BsonDocument>.Projection;

                    BsonDocument projfields = new BsonDocument();

                    foreach (var field in fields)
                    {
                        projfields[field] = 1;
                    }
                    projfields["_id"] = 1;
                    MongoDB.Driver.ProjectionDefinition<BsonDocument> projectionDefinition = projfields;
                    var ent = __db.Value.GetDocumentByUserFilter<BsonDocument>(__db.Config.entityname, ufilter, projfields);
                    return parseToJObject(ent);
                }
                else
                {
                    return parseToJObject(__db.Value.GetDocumentByUserFilter<BsonDocument>(__db.Config.entityname, ufilter));
                }
            });
        }
        /// <summary>
        /// 通过过滤条件获取一条数据
        /// db.GetOne("users",JObject.FromObject({username:'yyfs'}),fields).Result
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="filter"></param>
        /// <param name="fields"></param>
        /// <param name="unwindby"></param>
        /// <returns></returns>
        public async Task<JObject> GetOne(string entityname, JObject filter, List<string> fields, string unwindby = "")
        {
            return await Task.Run(() =>
            {
                var __db = InitDBConn(entityname);
                var queryfilter = FormatFilter(filter.ToString(Newtonsoft.Json.Formatting.None, null));
                FilterDefinition<BsonDocument> ufilter = queryfilter;
                bool useproj = false;
                MongoDB.Driver.ProjectionDefinition<BsonDocument> projectionDefinition = "{\"_id\":1}";
                if (fields != null && fields.Count > 0)
                {
                    ProjectionDefinitionBuilder<BsonDocument> builderProjection = Builders<BsonDocument>.Projection;
                    BsonDocument projfields = new BsonDocument();
                    foreach (var field in fields)
                    {
                        projfields[field] = 1;
                    }
                    projfields["_id"] = 1;
                    projectionDefinition = projfields;
                    useproj = true;

                }
                if (!string.IsNullOrEmpty(unwindby))
                {
                    var col_stages = new List<IPipelineStageDefinition>();
                    BsonDocument matchdoc = new BsonDocument();
                    matchdoc["$match"] = BsonDocument.Parse(queryfilter);

                    col_stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>(matchdoc.ToJson()));

                    BsonDocument unwinddoc = new BsonDocument();

                    if (!unwindby.StartsWith("$"))
                    {
                        unwindby = "$" + unwindby;
                    }

                    unwinddoc["$unwind"] = unwindby;
                    col_stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>(unwinddoc.ToJson()));

                    if (useproj)
                    {
                        BsonDocument projectdoc = new BsonDocument();
                        projectdoc["$project"] = projectionDefinition.ToBsonDocument()["Document"];
                        col_stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>(projectdoc.ToJson()));
                    }
                    var col_pipeline = new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(col_stages);
                    var ent = __db.Value.database.GetCollection<BsonDocument>(__db.Config.entityname).Aggregate(col_pipeline).FirstOrDefault();
                    if (ent != null)
                    {
                        return parseToJObject(ent);
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    if (useproj)
                    {
                        var ent = __db.Value.GetDocumentByUserFilter<BsonDocument>(__db.Config.entityname, ufilter, projectionDefinition);
                        return parseToJObject(ent);
                    }
                    else
                    {
                        return parseToJObject(__db.Value.GetDocumentByUserFilter<BsonDocument>(__db.Config.entityname, ufilter));
                    }
                }
            });
        }
        #endregion

        #region 新增数据
        /// <summary>
        /// 新增修改数据
        /// db.Save('users',JObject.FromObject(data),ret.optuser).Result;
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="entity"></param>
        /// <param name="optuser"></param>
        /// <returns></returns>
        public async Task<DBResult> Save(string entityname, JObject entity, string optuser)
        {
            return await Task.Run(() =>
            {
                var __db = InitDBConn(entityname);
                DBResult ret = new DBResult();
                ret.success = true;
                try
                {
                    MongoDB.Bson.BsonDocument document = ToBsonDocument(entity);

                    if (!document.Contains("_id"))
                    {
                        document["_id"] = new ObjectId();
                        document["create_at"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                        document["create_by"] = optuser;
                        __db.Value.Insert<BsonDocument>(__db.Config.entityname, document);
                    }
                    else
                    {
                        var result = Update(entityname, document["_id"].ToString(), entity, optuser);
                        if (!result.Result.success)
                        {
                            ret.success = false;
                            ret.message = result.Result.message;
                            ret.ex = result.Result.ex;
                        }
                    }
                    ret.entity = parseToJObject(document);
                }
                catch (Exception ex)
                {
                    ret.success = false;
                    ret.message = ex.Message;
                    ret.ex = ex;
                }
                return ret;
            });
        }
        #endregion

        #region 修改数据
        /// <summary>
        /// 更新排除项
        /// </summary>
        private List<string> updateExclude = new List<string> { "_id", "create_at", "create_by" };

        /// <summary>
        /// 更新单条数据
        /// db.UpdateOne("users",JObject.FromObject({username:'yyfs'}),JObject.FromObject({status:0}),ret.optuser).Result;
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="filterjo"></param>
        /// <param name="updatejo"></param>
        /// <param name="optuser"></param>
        /// <returns></returns>
        public async Task<DBResult> UpdateOne(string entityname, JObject filterjo, JObject updatejo, string optuser)
        {
            return await Task.Run(() =>
            {

                DBResult ret = new DBResult();
                ret.success = true;
                var __db = InitDBConn(entityname);
                try
                {
                    List<string> fields = new List<string>();
                    var filterdoc = ToBsonDocument(filterjo);

                    FilterDefinition<BsonDocument> filter = filterdoc;

                    bool upsert = true;


                    BsonDocument updatedocument = new BsonDocument();

                    foreach (var key in updateExclude)
                    {
                        if (updatejo.ContainsKey(key))
                        {
                            updatejo.Remove(key);
                        }
                    }

                    updatejo["update_at"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    updatejo["update_by"] = optuser;


                    if (updatejo["$upsert"] != null)
                    {
                        if ((updatejo["$upsert"] + "").ToLower() == "false")
                        {
                            upsert = false;
                        }
                        updatejo.Property("$upsert").Remove();
                    }

                    List<string> incprops = new List<string>();
                    if (updatejo["$inc"] != null)
                    {
                        updatedocument["$inc"] = ToBsonDocument((JObject)updatejo["$inc"]);

                        foreach (var incitem in (JObject)updatejo["$inc"])
                        {
                            incprops.Add(incitem.Key);
                        }

                        updatejo.Property("$inc").Remove();
                    }
                    List<string> unsetprops = new List<string>();
                    if (updatejo["$unset"] != null)
                    {
                        updatedocument["$unset"] = ToBsonDocument((JObject)updatejo["$unset"]);

                        foreach (var incitem in (JObject)updatejo["$unset"])
                        {
                            unsetprops.Add(incitem.Key);
                        }

                        updatejo.Property("$unset").Remove();
                    }
                    if (updatejo["$push"] != null)
                    {
                        //追加数组元素
                        updatedocument["$push"] = ToBsonDocument((JObject)updatejo["$push"]);
                        updatejo.Property("$push").Remove();
                    }
                    if (updatejo["$pull"] != null)
                    {
                        //删除数组元素
                        updatedocument["$pull"] = ToBsonDocument((JObject)updatejo["$pull"]);
                        updatejo.Property("$pull").Remove();
                    }
                    if (updatejo["$addToSet"] != null)
                    {
                        ///去重复追加元素
                        updatedocument["$addToSet"] = ToBsonDocument((JObject)updatejo["$addToSet"]);
                        updatejo.Property("$addToSet").Remove();
                    }
                    if (updatejo["$pop"] != null)
                    {
                        ///可以实现从数组中删除第一个或者是最后一个元素。
                        updatedocument["$pop"] = ToBsonDocument((JObject)updatejo["$pop"]);
                        updatejo.Property("$pop").Remove();
                    }
                    var createat = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    if (updatejo.HasValues)
                    {
                        updatedocument["$set"] = ToBsonDocument(updatejo);
                        var setOnInsertjo = new JObject();
                        setOnInsertjo["create_at"] = createat;
                        setOnInsertjo["create_by"] = optuser;

                        updatedocument["$setOnInsert"] = ToBsonDocument(setOnInsertjo);
                    }
                    FindOneAndUpdateOptions<BsonDocument> updateOptions = new FindOneAndUpdateOptions<BsonDocument>();

                    updateOptions.IsUpsert = upsert;
                    updateOptions.ReturnDocument = ReturnDocument.After;


                    BsonDocument adoc;
                    UpdateDefinition<BsonDocument> update = updatedocument;

                    var col_stages = new List<IPipelineStageDefinition>();

                    JObject newupdatejo = new JObject();
                    if (updatejo["$aggregation"] != null)
                    {
                        //无法进行差异
                        foreach (var tk in updatejo["$aggregation"])
                        {
                            col_stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>(tk.ToString()));

                            if (tk["$set"] != null)
                            {
                                newupdatejo = (JObject)tk["$set"];
                            }
                            //    pipeline.AppendStage(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>(tk.ToString()));
                        }

                        var col_pipeline = new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(col_stages);
                        adoc = __db.Value.FindOneAndUpdate<BsonDocument>(__db.Config.entityname, filter, col_pipeline, updateOptions);
                        updatejo.Property("$aggregation").Remove();

                        updatejo = newupdatejo;
                    }
                    else
                    {
                        adoc = __db.Value.FindOneAndUpdate<BsonDocument>(__db.Config.entityname, filter, update, updateOptions);
                    }
                    ret.entity = parseToJObject(adoc);
                }
                catch (Exception ex)
                {
                    ret.success = false;
                    ret.message = ex.Message;
                    ret.ex = ex;
                }
                return ret;
            });
        }
        /// <summary>
        /// 更新数据
        /// db.Update("users","xxxxxxxx",JObject.FromObject({status:0}),ret.optuser).Result;
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="_id"></param>
        /// <param name="updatejo"></param>
        /// <param name="optuser"></param>
        /// <returns></returns>
        public async Task<DBResult> Update(string entityname, string _id, JObject updatejo, string optuser)
        {
            return await Task.Run(() =>
            {
                return UpdateOne(entityname, JObject.Parse("{\"_id\":{\"$oid\":\"" + _id + "\"}}"), updatejo, optuser).Result;
            });
        }

        #region 根据条件批量更新数据
        /// <summary>
        /// 根据条件批量更新数据
        /// db.UpdateMany("users",JObject.FromObject({status:0}),JObject.FromObject({status:1}),ret.optuser).Result;
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="filterjo"></param>
        /// <param name="updatejo"></param>
        /// <param name="optuser"></param>
        /// <param name="option"></param>
        /// <returns></returns>
        public async Task<DBResult> UpdateMany(string entityname, JObject filterjo, JObject updatejo, string optuser, JObject option)
        {
            return await Task.Run(() =>
            {

                DBResult ret = new DBResult();
                ret.success = true;
                var __db = InitDBConn(entityname);
                try
                {
                    List<string> fields = new List<string>();
                    var filterdoc = ToBsonDocument(filterjo);
                    FilterDefinition<BsonDocument> filter = filterdoc;
                    bool upsert = false;
                    if (option != null && option["$upsert"] != null)
                    {
                        if ((option["$upsert"] + "").ToLower() == "true")
                        {
                            upsert = true;
                        }
                    }

                    BsonDocument updatedocument = new BsonDocument();

                    foreach (var key in updateExclude)
                    {
                        if (updatejo.ContainsKey(key))
                        {
                            updatejo.Remove(key);
                        }
                    }

                    updatejo["update_at"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    updatejo["update_by"] = optuser;

                    List<string> incprops = new List<string>();
                    if (updatejo["$inc"] != null)
                    {
                        updatedocument["$inc"] = ToBsonDocument((JObject)updatejo["$inc"]);

                        foreach (var incitem in (JObject)updatejo["$inc"])
                        {
                            incprops.Add(incitem.Key);
                        }

                        updatejo.Property("$inc").Remove();
                    }
                    List<string> unsetprops = new List<string>();
                    if (updatejo["$unset"] != null)
                    {
                        updatedocument["$unset"] = ToBsonDocument((JObject)updatejo["$unset"]);

                        foreach (var incitem in (JObject)updatejo["$unset"])
                        {
                            unsetprops.Add(incitem.Key);
                        }

                        updatejo.Property("$unset").Remove();
                    }
                    if (updatejo["$push"] != null)
                    {
                        //追加数组元素
                        updatedocument["$push"] = ToBsonDocument((JObject)updatejo["$push"]);
                        updatejo.Property("$push").Remove();
                    }
                    if (updatejo["$pull"] != null)
                    {
                        //删除数组元素
                        updatedocument["$pull"] = ToBsonDocument((JObject)updatejo["$pull"]);
                        updatejo.Property("$pull").Remove();
                    }
                    if (updatejo["$addToSet"] != null)
                    {
                        ///去重复追加元素
                        updatedocument["$addToSet"] = ToBsonDocument((JObject)updatejo["$addToSet"]);
                        updatejo.Property("$addToSet").Remove();
                    }
                    if (updatejo["$pop"] != null)
                    {
                        ///可以实现从数组中删除第一个或者是最后一个元素。
                        updatedocument["$pop"] = ToBsonDocument((JObject)updatejo["$pop"]);
                        updatejo.Property("$pop").Remove();
                    }
                    var createat = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    if (updatejo.HasValues)
                    {
                        updatedocument["$set"] = ToBsonDocument(updatejo);
                        var setOnInsertjo = new JObject();
                        setOnInsertjo["create_at"] = createat;
                        setOnInsertjo["create_by"] = optuser;

                        updatedocument["$setOnInsert"] = ToBsonDocument(setOnInsertjo);
                    }


                    UpdateOptions updateOptions = new UpdateOptions();
                    updateOptions.IsUpsert = upsert;

                    UpdateDefinition<BsonDocument> update = updatedocument;

                    var col_stages = new List<IPipelineStageDefinition>();

                    JObject newupdatejo = new JObject();
                    if (updatejo["$aggregation"] != null)
                    {
                        //无法进行差异
                        foreach (var tk in updatejo["$aggregation"])
                        {
                            col_stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>(tk.ToString()));

                            if (tk["$set"] != null)
                            {
                                newupdatejo = (JObject)tk["$set"];
                            }
                            //    pipeline.AppendStage(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>(tk.ToString()));
                        }

                        var col_pipeline = new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(col_stages);
                        __db.Value.UpdateMany<BsonDocument>(__db.Config.entityname, filter, col_pipeline, updateOptions);
                        updatejo.Property("$aggregation").Remove();

                        updatejo = newupdatejo;
                    }
                    else
                    {
                        __db.Value.UpdateMany<BsonDocument>(__db.Config.entityname, filter, update, updateOptions);
                    }

                }
                catch (Exception ex)
                {
                    ret.success = false;
                    ret.message = ex.Message;
                    ret.ex = ex;
                }
                return ret;
            });
        }
        #endregion
        #endregion

        #region 删除数据
        /// <summary>
        /// 删除数据
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="_id"></param>
        /// <param name="optuser"></param>
        /// <param name="real">真实删除</param>
        /// <returns></returns>
        public async Task<DBResult> Delete(string entityname, string _id, string optuser, bool real = false)
        {
            return await Task.Run(() =>
            {
                var ret = new DBResult();
                ret.success = true;
                try
                {
                    var __db = InitDBConn(entityname);

                    if (real)
                    {// 物理删除（真实删除）
                        __db.Value.Delete<BsonDocument>(__db.Config.entityname, _id);
                    }
                    else
                    {// 逻辑删除
                        FilterDefinition<BsonDocument> filter = "{\"_id\":ObjectId('" + _id + "')}";

                        BsonDocument updateent = new BsonDocument();
                        BsonDocument setent = new BsonDocument();
                        setent["sys_isdelete"] = true;
                        setent["sys_isdelete_time"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                        setent["sys_deleteby"] = optuser;
                        updateent["$set"] = setent;

                        UpdateDefinition<BsonDocument> update = updateent;
                        UpdateOptions uo = new UpdateOptions();
                        uo.IsUpsert = false;
                        __db.Value.Update<BsonDocument>(__db.Config.entityname, filter, update, uo);
                    }

                }
                catch (Exception ex)
                {
                    ret.success = false;
                    ret.message = ex.Message;
                    ret.ex = ex;
                }
                return ret;
            });
        }
        #endregion

        #region 聚合查询
        /// <summary>
        /// 聚合查询
        /// db.aggregate('users',true,data).Result;
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="getlist"></param>
        /// <param name="pipelines"></param>
        /// <returns></returns>
        public async Task<AggregateOutput> aggregate(string entityname, bool getlist, object pipelines)
        {
            return await Task.Run(() =>
            {
                QueryAggregate agg = new QueryAggregate();
                if (getlist)
                {
                    agg.querytype = AggregateQueryType.list;
                }
                else
                {
                    agg.querytype = AggregateQueryType.getone;
                }

                agg.pipelines = JArray.FromObject(pipelines).ToObject<List<JObject>>();
                return Aggregate(entityname, agg);
            });
        }
        /// <summary>
        /// 聚合查询
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="agg"></param>
        /// <returns></returns>
        public async Task<AggregateOutput> Aggregate(string entityname, QueryAggregate agg)
        {
            return await Task.Run(() =>
            {
                var __db = InitDBConn(entityname);
                AggregateOutput output = new AggregateOutput();

                IList<BsonDocument> list;


                var col_stages = new List<IPipelineStageDefinition>();
                foreach (var line in agg.pipelines)
                {
                    col_stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>(line.ToString()));
                }

                var col_pipeline = new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(col_stages);
                if (agg.querytype == AggregateQueryType.list)
                {
                    list = __db.Value.database.GetCollection<BsonDocument>(__db.Config.entityname).Aggregate(col_pipeline).ToList();
                    output.data = parseToJArray(list);
                }
                else if (agg.querytype == AggregateQueryType.getone)
                {
                    var ent = __db.Value.database.GetCollection<BsonDocument>(__db.Config.entityname).Aggregate(col_pipeline).FirstOrDefault();
                    if (ent != null)
                    {
                        output.data = parseToJObject(ent);
                    }
                }
                return output;
            });
        }

        public async Task<long> Count(string entityname, object filterjo, string optuser)
        {
            return await Task.Run(() =>
            {
                var __db = InitDBConn(entityname);
                var filterdoc = ToBsonDocument(JObject.FromObject(filterjo));



                var queryfilter = FormatFilter("", filterdoc);


                FilterDefinition<BsonDocument> ufilter = queryfilter;

                var total = __db.Value.GetCount(__db.Config.entityname, ufilter);

                return total;
            });
        }

        /// <summary>
        /// 求和(单字段)
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="filterjo"></param>
        /// <param name="groupby"></param>
        /// <param name="sumfield"></param>
        /// <param name="optuser"></param>
        /// <returns></returns>
        public async Task<double> Sum(string entityname, object filterjo, string groupby, string sumfield, string optuser)
        {
            return await Task.Run(() =>
            {

                var __db = InitDBConn(entityname);

                var filterdoc = ToBsonDocument(JObject.FromObject(filterjo));



                var queryfilter = FormatFilter("", filterdoc);


                var col_stages = new List<IPipelineStageDefinition>();

                col_stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>("{\"$match\":" + queryfilter + "}"));

                col_stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>("{\"$group\":{\"_id\":\"$" + groupby + "\",\"sum\":{\"$sum\":\"$" + sumfield + "\"}}}"));

                var col_pipeline = new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(col_stages);
                var ent = __db.Value.database.GetCollection<BsonDocument>(__db.Config.entityname).Aggregate(col_pipeline).FirstOrDefault();
                if (ent != null)
                {
                    if (ent.Contains("sum") && ent["sum"] != null)
                    {
                        return double.Parse(ent["sum"].ToString());
                    }
                }
                return 0;
            });
        }

        /// <summary>
        /// 求和(多字段字段)
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="filterjo"></param>
        /// <param name="groupby"></param>
        /// <param name="sumfields"></param>
        /// <param name="optuser"></param>
        /// <returns></returns>

        public async Task<object> Sums(string entityname, object filterjo, object groupby, object sumfields, string optuser)
        {
            return await Task.Run(() =>
            {
                var __db = InitDBConn(entityname);
                var filterdoc = ToBsonDocument(JObject.FromObject(filterjo));
                var queryfilter = FormatFilter("", filterdoc);

                return Sums(entityname, queryfilter, groupby, sumfields, optuser);

            });
        }
        private async Task<object> Sums(string entityname, string queryfilter, object groupby, object sumfields, string optuser)
        {
            return await Task.Run(() =>
            {
                var __db = InitDBConn(entityname);
                List<string> fields = JArray.FromObject(sumfields).ToObject<List<string>>();
                var col_stages = new List<IPipelineStageDefinition>();
                col_stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>("{\"$match\":" + queryfilter + "}"));

                var groupjo = new JObject();
                if (groupby == null)
                {
                    groupjo["_id"] = null;
                }
                else
                {
                    List<string> groupbys = (List<string>)groupby;
                    if (groupbys.Count == 0)
                    {
                        groupjo["_id"] = null;
                    }
                    else
                    {
                        var group_id = new JObject();
                        foreach (var gk in groupbys)
                        {
                            group_id[gk] = "$" + gk;
                        }
                        groupjo["_id"] = group_id;
                    }
                }
                foreach (var f in fields)
                {
                    groupjo[f] = JObject.Parse("{\"$sum\":\"$" + f + "\"}");
                }
                var _group = new JObject();
                _group["$group"] = groupjo;

                col_stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>(_group.ToString(Newtonsoft.Json.Formatting.None, null)));

                var col_pipeline = new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(col_stages);
                var ent = __db.Value.database.GetCollection<BsonDocument>(__db.Config.entityname).Aggregate(col_pipeline).ToList();

                return parseToJArray(ent);
            });
        }
        #endregion

        #region 创建删除索引
        /// <summary>
        /// 创建索引
        /// </summary>
        /// <param name="entityname"></param>
        /// <returns></returns>
        public async Task<JArray> GetDbIndexs(string entityname)
        {
            return await Task.Run(() =>
            {
                var __db = InitDBConn(entityname);
                var client = new MongoClient(__db.Config.conn);
                IMongoDatabase database = client.GetDatabase(__db.Config.dbname);
                var coll = database.GetCollection<BsonDocument>(__db.Config.entityname);
                var list = coll.Indexes.List().ToList<BsonDocument>();
                return parseToJArray(list);
            });
        }
        /// <summary>
        /// 创建索引
        /// </summary>
        /// <param name="entityname"></param>
        /// <returns></returns>
        public async Task CreateDbIndex(string entityname, string indexname, List<IndexKeyDto> keys)
        {

            var __db = InitDBConn(entityname);
            var client = new MongoClient(__db.Config.conn);
            IMongoDatabase database = client.GetDatabase(__db.Config.dbname);

            var coll = database.GetCollection<BsonDocument>(__db.Config.entityname);
            JObject keysjo = new JObject();
            foreach (var key in keys)
            {
                keysjo[key.key] = key.value;
            }
            IndexKeysDefinition<BsonDocument> ikeys = keysjo.ToString(Newtonsoft.Json.Formatting.None, null);
            CreateIndexOptions op = new CreateIndexOptions();
            op.Name = indexname;
            op.Background = true;
            CreateIndexModel<BsonDocument> cb = new CreateIndexModel<BsonDocument>(ikeys, op);
            await coll.Indexes.CreateOneAsync(cb);
        }
        /// <summary>
        /// 删除索引
        /// </summary>
        /// <param name="entityname"></param>
        /// <param name="indexname"></param>
        /// <returns></returns>
        public async Task DropDbIndex(string entityname, string indexname)
        {
            var __db = InitDBConn(entityname);
            var client = new MongoClient(__db.Config.conn);
            IMongoDatabase database = client.GetDatabase(__db.Config.dbname);
            var coll = database.GetCollection<BsonDocument>(__db.Config.entityname);
            await coll.Indexes.DropOneAsync(indexname);
        }
        #endregion

    }
}
